module Reactive
  class << self
    def load(argv = ARGV)
      return @bootstrapper if @bootstrapper
      options = {}
      options[:root_dir] = if index = argv.rindex(argv.find {|arg| arg == '--root' })
        argv[index+1]
      end
      options[:config_file] = if index = argv.rindex(argv.find {|arg| arg == '--config-file' })
        argv[index+1]
      end
      @argv = argv
      @bootstrapper ||= Bootstrapper.new(options)
    end
  
    def loaded?
      @bootstrapper
    end
  
    def init(stage = :final, argv = @argv || ARGV)
      load(argv)
      Reactive::Initializer.run(stage, Configuration.new(argv))
    end
    
    def run(argv = @argv || ARGV)
      init(:final, argv)
      Dispatcher::Base.dispatch(Reactive.configuration.initial_request.is_a?(Hash) ? Request.new(Reactive.configuration.initial_request) : Reactive.configuration.initial_request)
    end
  end

  class Bootstrapper
    def initialize(options)
      @config_file = options[:config_file]
      @root_dir = File.expand_path(options[:root_dir]) if options[:root_dir]
      load_rubygems
      
      # add apps gem directory to load path
      if @root_dir
        Gem.clear_paths
        Gem.path.unshift("#{@root_dir}/gems")
      end
      
      load_reactive_gem
      require 'reactive-core/initializer'
    end

    def load_reactive_gem
      # load activesupport first
      begin
        if version = activesupport_gem_version
          gem 'activesupport', version
        end
      rescue Gem::LoadError => load_error
        $stderr.puts "Missing the activesupport #{version} gem. Either install the required version or choose an appropriate value for the ACTIVESUPPORT_GEM_VERSION setting in config/config.rb"
        exit 1
      end
    
      if version = reactive_gem_version
        gem 'reactive-core', version
      else
        gem 'reactive-core'
      end
    rescue Gem::LoadError => load_error
      $stderr.puts "Missing the reactive-core #{version} gem. Please `gem install -v=#{version} reactive-core`, update your REACTIVE_GEM_VERSION setting in config/config.rb for the Reactive version you do have installed, or comment out REACTIVE_GEM_VERSION to use the latest version installed."
      exit 1
    end

    def rubygems_version
      Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
    end

    def reactive_gem_version
      if defined? REACTIVE_GEM_VERSION
        REACTIVE_GEM_VERSION
      elsif ENV.include?('REACTIVE_GEM_VERSION')
        ENV['REACTIVE_GEM_VERSION']
      else
        parse_gem_version('reactive', read_config_rb)
      end
    end
  
    def activesupport_gem_version
      parse_gem_version('activesupport', read_config_rb)
    end

    def load_rubygems
      require 'rubygems'

      unless rubygems_version >= '1.1.0'
        $stderr.puts "Reactive requires RubyGems >= 1.1.0 (you have #{rubygems_version}). Please `gem update --system` and try again."
        exit 1
      end

    rescue LoadError
      $stderr.puts "Reactive requires RubyGems >= 1.1.0. Please install RubyGems and try again: http://rubygems.rubyforge.org"
      exit 1
    end

    def parse_gem_version(gem_name, text)
      $1 if text =~ Regexp.new(%Q{^[^#]*#{gem_name.upcase}_GEM_VERSION\\s*=\\s*["']([!~<>=]*\\s*[\\d.]+)["']})
    end

    private
      def read_config_rb
        return if @config_rb
        files = [
          @config_file,
          @root_dir ? "#{@root_dir}/config/config.rb" : nil,
          @root_dir ? "#{@root_dir}/config.rb" : nil,
          "config.rb"
        ].compact.select {|name| File.file? name}
        
        raise StandardError, "No config file found, you may specify it with --config-file" if files.empty?
        @config_rb = File.read(files.first)
      end
  end

end
